﻿
// opencv_demo1.cpp: 定义应用程序的类行为。

#include <vector>
#include "watermask_cv_dct.h"

#if 1
watermask_cv_dct::watermask_cv_dct()
{
	
}
void watermask_cv_dct::loadWatermask(std::string waterMaskFile)
{
	if (m_watermask.empty()) {
		//m_watermask = getInformationOfBinaryGraph("C:/Users/hanbo/Desktop/test/cs.bmp");
		m_watermask = getInformationOfBinaryGraph(waterMaskFile);
		if (m_watermask.empty()) {
			printf("logo file error: %s\n", waterMaskFile.c_str());
		}
		else {
			printf("load logo success:%s, width:%d,height:%d\n", waterMaskFile.c_str(), m_water_width, m_water_height);
		}
	}
}
bool watermask_cv_dct::has() 
{
	return m_watermask.empty() ? false : true;
}
Mat watermask_cv_dct::addImageWatermarkWithText(Mat image)
{
	return addImageWatermarkWithText(image, m_watermask, (int)m_watermask.size(),m_deep);
}
/**
	 * 嵌入水印信息
	 * @param image：原图
	 * @param watermark：水印信息,为信息数组
	 * @param p：嵌入强度
	 */
void watermask_cv_dct::addImageWatermarY(char* yuv, int _w, int _h)
{
	if (m_watermask.empty()) {
		return;
	}

	//添加水印到 yuv 视频数据

	//方法1 yuv 转 bgr 
	//cv::Mat mainYUuvImage(yuv_height * 3 / 2, yuv_width, CV_8UC1, (unsigned char*)yuv);
	//cv::Mat mainRgbImage;
	//cv::cvtColor(mainYUuvImage, mainRgbImage, CV_YUV2BGR_I420);//CV_YUV420p2RGB
	//Mat outImg = m_waterMaskObj.addImageWatermarkWithText(mainRgbImage);

	//putText(outImg, "ceshixinx", Point(40,40), FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0));

	//cv::cvtColor(outImg, mainYUuvImage, CV_BGR2YUV_I420);
	//memcpy(yuv, mainYUuvImage.data, yuv_width * yuv_height * 3 / 2);

	//方法2	
	cv::Mat mainYUuvImage(_h, _w, CV_8UC1, (unsigned char*)yuv);
	//watermask_cv_dct::myPrintCurTime("begin");
	addImageWatermarY(mainYUuvImage);
	//watermask_cv_dct::myPrintCurTime("end");

	memcpy(yuv, mainYUuvImage.data, _w * _h);

	//提取水印
	//GetWarterMarkYUV(yuv, yuv_width, yuv_height);
}
 Mat watermask_cv_dct::addImageWatermarkWithText(Mat image, std::vector<int> &watermark,int waterMarkLen, double p)
{
	std::vector<Mat> allPlanes;

	Mat yuvMat(image.size(),CV_8UC3);
	cvtColor(image, yuvMat, CV_BGR2YUV_I420);
	split(yuvMat, allPlanes);
	//获取YMat矩阵
	Mat YMat = allPlanes.at(0);
	//Mat YMat2(YMat.size(),CV_8UC1);

	addImageWatermarY(YMat, watermark, waterMarkLen, p);

	merge(allPlanes, yuvMat);
	Mat newMat;
	cvtColor(yuvMat, newMat, CV_YUV2BGR_I420);
	return newMat;
}

Mat watermask_cv_dct::addImageWatermarY(Mat imageY)
{
	return addImageWatermarY(imageY, m_watermask, (int)m_watermask.size(), m_deep);
}
Mat watermask_cv_dct::addImageWatermarY(Mat imageY, std::vector<int>& watermark, int waterMarkLen, double p)
 {
	 if (imageY.rows / m_blockLen * imageY.cols / m_blockLen < waterMarkLen) {
		 //ASSERT(0);
		 printf("addImageWatermarY error: watermask len big long");
		 return Mat(); //返回空图
	 }

	 //分成4096块
	 for (int i = 0; i < waterMarkLen; i++) {
		 //block 表示分块 而且为 方阵
		 //提取每个分块
		 Mat block = getBlock(imageY, i, m_blockLen);

		 //int x1 = 1, y1 = 2;
		 //int x2 = 2, y2 = 1;
		 Mat DCTY(block.size(), CV_64FC1);
		 //对分块进行DCT变换
		 dct(Mat_<double>(block), DCTY);

		 //添加水印方法1
		 /*if (watermark[i] == 1) {
			 DCTY.at<double>(m_x, m_y) = p;
			 DCTY.at<double>(m_x2, m_y2) = 0;
		 }
		 else {
			 DCTY.at<double>(m_x, m_y) = 0;
			 DCTY.at<double>(m_x2, m_y2) = p;
		 }*/
		 //添加水印方法2
		 double r1 = DCTY.at<double>(m_x, m_y);
		 double r2 = DCTY.at<double>(m_x2, m_y2);
		 double detat = abs(r1 - r2); //水印深度
		 if (detat < m_deep) {
			 detat = m_deep;
		 }
		 if (watermark[i] == 1) {  //1白色, 指纹主体, 用r1 > r2来记录
			 if (r1 <= r2) {
				 DCTY.at<double>(m_x, m_y) += detat;
			 }
		 }
		 else {
			 if (r1 >= r2) {   //0黑色, 用r1 < r2来记录
				 DCTY.at<double>(m_x2, m_y2) += detat;
			 }
		 }

		 //对上面分块进行IDCT变换
		 idct(DCTY, DCTY);

		 //保存
		 saveBlock(imageY, DCTY, i, m_blockLen);
	 }
	 return imageY;
 }

/**
 * 提取水印信息
 * @param image：带提取的图片
 * @return int[][]
 */
 Mat watermask_cv_dct::getImageWatermarkWithText(Mat image,int water_w,int water_h)
{
	std::vector<Mat> allPlanes;

	Mat Ycbcr(image.rows, image.cols, CV_8UC1);
	cvtColor(image, Ycbcr, CV_BGR2YUV_I420);
	split(image, allPlanes);

	Mat YMat = allPlanes.at(0);
	std::vector<int> watermark(water_w * water_h);

	//分成64块，提取每块嵌入的水印信息
	for (int i = 0; i < water_w * water_h; i++) {
		//block 表示分块 而且为 方阵
		//提取每个分块
		Mat block = getBlock(YMat, i, m_blockLen);

		//对分块进行DCT变换
		Mat DCTY(block.size(), CV_64FC1);
		dct(Mat_<double>(block), DCTY);
		//用于容纳DCT系数
		//int x1 = 1, y1 = 2;
		//int x2 = 2, y2 = 1;

		double a = DCTY.at<double>(m_x, m_y);
		double c = DCTY.at<double>(m_x2, m_y2);

		if (a >= c) {
			watermark[i] = 1;
		}else {
			watermark[i] = 0;
		}
	}

	//保存水印为Mat
	Mat binaryPhoto(water_h, water_w, THRESH_BINARY);
	int index = 0;
	for (int i = 0; i < water_h; i++) {
		for (int j = 0; j < water_w; j++) {
			if (watermark[index] == 1) {
				binaryPhoto.at<uchar>(i, j) = 255;
			}
			else {
				binaryPhoto.at<uchar>(i, j) = 0;
			}
			index++;
		}
	}
	return binaryPhoto;
}

/**
 * 提取每个分块。将原图按照length，分成多个分块
 * @param YMat：原分块
 * @param x：第几个块
 * @param length：每个块的长度（正方形）
 * @return
 */
Mat watermask_cv_dct::getBlock(Mat YMat, int index, int blockLen) {
	Mat mat(blockLen, blockLen, CV_8UC1);

	int rowBlockCount = YMat.cols / blockLen;  //一行的最大块数

	int x = index / rowBlockCount;   //行
	int y = index % rowBlockCount;   //列

	for (int i= 0; i < blockLen; i++) {
		for (int j= 0; j < blockLen; j++) {
			mat.at<uchar>(i, j) = YMat.at<uchar>(x * blockLen + i, y * blockLen + j);
		}
	}
	return mat;
}
void watermask_cv_dct::saveBlock(Mat YMat, Mat& dctBlockMat, int index, int blockLen) {
	int rowBlockCount = YMat.cols / blockLen;  //一行的最大块数

	int x = index / rowBlockCount;   //行
	int y = index % rowBlockCount;   //列

	for (int i = 0; i < blockLen; i++) {
		for (int j = 0; j < blockLen; j++) {
			YMat.at<uchar>(x * blockLen + i, y * blockLen + j) = dctBlockMat.at<double>(i, j);
		}
	}
}

/**
 * 获取二值图的信息
 */
std::vector<int> watermask_cv_dct::getInformationOfBinaryGraph(String srcPath)
{
	std::vector<int>  info; 

	Mat mat = imread(srcPath);
	if(mat.empty()){
		return info;
	}

	//使用图片尺寸
	m_water_width = mat.cols;
	m_water_height = mat.rows;
	
	//resize(mat, mat,Size(water_height, water_width)); //使用配置尺寸 二选一

	cvtColor(mat, mat, CV_BGR2GRAY);
	cv::threshold(mat, mat, 128, 255, THRESH_BINARY); //黑白图
	//imshow("watermask_orig", mat);
	//waitKey(0);
	
	for (int i = 0; i < m_water_height; i++) {
		for (int j = 0; j < m_water_width; j++) {
			if (mat.at<uchar>(i, j) == 255)
				info.push_back(1);
			else
				info.push_back(0);
		}
	}
	return info;
}



/**
 * 将一张图片压缩成一张64x64的二值图
 * @param srcPath
 * @param dstPath
 */
#if 0
public static String getBinaryPhoto(String srcPath, String dstPath) {

	srcPath = thumbnail(srcPath, dstPath, 64, 64);

	//得到原图
	File file = new File(srcPath);
	BufferedImage image = null;
	try {
		image = ImageIO.read(file);
	}
	catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	int width = image.getWidth();
	int height = image.getHeight();
	//创建原图的二值图
	BufferedImage binaryPhoto = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
	int min = new Color(0, 0, 0).getRGB();
	int max = new Color(255, 255, 255).getRGB();
	//判断标记
	int flag = 170;
	for (int i = 0; i < width; i++) {
		for (int j = 0; j < height; j++) {
			//像素
			int pixel = image.getRGB(i, j);
			//得到 rgb通道对应的元素
			int r, g, b;
			r = (pixel & 0xff0000) >> 16;
			g = (pixel & 0xff00) >> 8;
			b = (pixel & 0xff);
			int avg = (r + g + b) / 3;
			if (avg <= flag)
				binaryPhoto.setRGB(i, j, min);
			else
				binaryPhoto.setRGB(i, j, max);
		}
	}
	try {
		ImageIO.write(binaryPhoto, "bmp", new File(dstPath));
	}
	catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}
	return dstPath;
}



/**
 * 将图片变成指定大小的图片
 */
public static String thumbnail(String srcImagePath, String desImagePath, int w, int h) {

	Mat src = Imgcodecs.imread(srcImagePath);
	Mat dst = src.clone();
	Imgproc.resize(src, dst, new Size(w, h));
	Imgcodecs.imwrite(desImagePath, dst);
	return desImagePath;
}
#endif

#endif
//////////////////////////////////////////////   人脸识别   ///////////////////////////////////////////////
cv_face_find* cv_face_find::getInstance()
{
	static cv_face_find* g_pinst = nullptr;
	if (nullptr == g_pinst) {
		g_pinst = new cv_face_find();
	}
	return g_pinst;
}
void cv_face_find::load()
{
	faceCascade.load("./opencv454_config_data/haarcascades/haarcascade_frontalface_alt2.xml");
	//faceCascade.load("./opencv454_config_data/haarcascades/haarcascade_eye.xml");
}
void cv_face_find::findface(char* yuv, int _w, int _h)
{
	//测试通过yuv 失败人像.可以使用参加摄像头直接测试
	//Mat img = imread("C:\\Users\\hanbo\\Desktop\\test\\lana.png");
	//if (img.empty()) {
	//	return;
	//}
	cv::Mat imgGray;
	cv::Mat yuvMat(_h, _w, CV_8UC1, yuv);

	imgGray = yuvMat;
	//周期检测
	if (0 == m_index) {
		cv::Mat testimgGray;
		std::vector<cv::Rect> testfaces;
		//cv::resize(yuvMat, testimgGray,cv::Size(_w/4, _h/4), 0, 0, INTER_LINEAR);
		//equalizeHist(testimgGray, testimgGray); //???????
		if(_h >= 1080){
			faceCascade.detectMultiScale(imgGray, m_faces, 1.4, 3, 0, Size(0, 0));   //检测人脸
		}else{
			faceCascade.detectMultiScale(imgGray, m_faces, 1.2, 3, 0, Size(10, 10));
		}

#if 0
		m_faces.clear();
		if (testfaces.size() > 0) {
			auto facsIter = testfaces.begin();
			cv::Rect testRect;
			for (; facsIter != testfaces.end(); ++facsIter) {
				testRect.x = facsIter->x *4;
				testRect.y = facsIter->y * 4;
				testRect.width = facsIter->width * 4;
				testRect.height = facsIter->height * 4;
				m_faces.push_back(testRect);
			}
		}
#endif
		m_index++;
	} else if (2 == m_index) {
		m_index = 0;
	}else {
		m_index++;
	}
	
	if (m_faces.size() > 0) {
		for (int i = 0; i < (int)m_faces.size(); i++) {
			rectangle(imgGray, Point(m_faces[i].x, m_faces[i].y), Point(m_faces[i].x + m_faces[i].width, m_faces[i].y + m_faces[i].height),
				Scalar(100, 255, 0), 2, 8);    //框出人脸位置
		}
	}

	//imshow("FacesOfPrettyGirl", yuvMat);
	//waitKey(0);
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void cv_map_picture::loadpic()
{
  // cv::Mat loadpic = cv::imread("host.jpg");  //加载图片
	//cv::resize(loadpic, loadpic, cv::Size(100, 100));
	cv::Mat loadpic(100, 100, CV_8UC3, Scalar(0, 0, 0));

   cv::cvtColor(loadpic, m_smallpic, cv::COLOR_RGB2YUV);

   std::vector<Mat> pics;
   cv::split(m_smallpic, pics);
   m_smallpic = pics[0];   //只要y分量

  // cv::imshow("smallpic", smallpic);
 
}
void cv_map_picture::test(char* yuv, int _w, int _h)  //贴图
{
	cv::Mat YUuvImage(_h, _w, CV_8UC1, (unsigned char*)yuv); //只有y分量
	//cv::Mat YUuvImage(_h + _h / 2, _w, CV_8UC1, (unsigned char*)yuv);

	cv::Mat subImg = YUuvImage(cv::Rect(20, 20, 100, 100));
	m_smallpic.copyTo(subImg);
}